package WebService::RallyDev;
use strict;
use warnings;
use Carp::Assert;

=head1 NAME

WebService::RallyDev - Perl Client for RallyDev Webservice

=head1 VERSION

0.01

=head1 DESCRIPTION

WebService::RallyDev provides a simple object oriented perl interface to
the Rally webservice API.

=head1 SYNOPSIS

use WebService::RallyDev;

my $rally = WebService::RallyDev->new($rally_username,$rally_password);

my $iteration = $rally->get_current_iteration || die Dumper($rally->last_request_errors);

my $stories = $rally->get_stories_for_iteration({
    iteration_url => $iteration->url,
    pagesize => 100
});

=cut

use HTTP::Request::Common;
use URI::Query;
use LWP::UserAgent;
use JSON -support_by_pp;
use utf8;
use Class::Date qw(now);
use Data::Dumper;

use WebService::RallyDev::Iteration;
use WebService::RallyDev::UserStory;
use WebService::RallyDev::Task;

our $VERSION = '0.01';
our $DEBUG=0;

$Class::Date::DATE_FORMAT="%Y-%m-%dT%H:%M:%S";

my $webservice_urls = {
		       community => {
				     iteration_url => 'https://community.rallydev.com/slm/webservice/1.14/iteration.js',
				     story_url => 'https://community.rallydev.com/slm/webservice/1.14/hierarchicalrequirement.js',
				     task_url => 'https://community.rallydev.com/slm/webservice/1.14/task.js',
				    },
		       paid => {

			       },
		      };

sub new {
    my $class = shift;
    my $username = shift;
    my $password = shift;
    my $subscription = shift || 'community';
    my $self={ subscription => $subscription };
    bless $self, $class;
    $self->{username}=$username if $username;
    $self->{password}=$password if $password;

    return $self;
}

sub subscription {
    return shift()->{subscription};

}

sub username {
    my $self = shift;
    my $username = shift;

    if ($username) {
        $self->{username}=$username;
    }
    return $self->{username};
}

sub password {
    my $self = shift;
    my $password = shift;

    if ($password) {
        $self->{password}=$password;
    }
    return $self->{password};
}

sub get_current_iteration {
    my $self = shift;
    my $now = now();

    my %iteration_args = (
                        query=>"(( EndDate >= $now) and (StartDate <= $now))",
                        PageSize=>200,
                        fetch=>'true',
                    );
    my $result = $self->_GET_request('iteration_url',\%iteration_args);

    my $count = $result->{QueryResult}->{TotalResultCount};
    if ($count ==0) {
        warn "Cannot find current iteration";
        #perhaps we are between iterations, so lets get the previous iteration
        $now = $now - '7D';
        $iteration_args{query}="(( EndDate >= $now) and (StartDate <= $now))";
        $result = $self->_GET_request('iteration_url',\%iteration_args);
        $count = $result->{QueryResult}->{TotalResultCount};
        unless ($count == 1) {
            warn "Failed to load a previous iteration either";
            return undef;
        }
    } elsif($count > 1) {
        warn "Too many results returned for get_current_iteration ($count)";
        return undef;
    }

    my $iteration = WebService::RallyDev::Iteration->new($result->{QueryResult}->{Results}->[0]);
    return $iteration;
}


sub get_stories_for_iteration {
    my $self = shift;
    my $args = shift;

    my $iteration_url = $args->{iteration_url} || die "no iteration url provided\n";
    my $owner = $args->{owner};
    my $pagesize = $args->{pagesize} || 100;
    my $order = $args->{order} || 'Rank';

    my $query ='';
    if ($owner) {
        $query = "(( Owner = $owner ) and ( Iteration = $iteration_url ))";
    } else {
        $query = "( Iteration = $iteration_url )";
    }
    
    my %args = (
                    query=>$query,
                    pagesize=>$pagesize,
                    order=>$order,
                    fetch=>'true',
                );
    my $result = $self->_GET_request('story_url',\%args);
    #print STDERR Dumper($result);

    my $count = $result->{QueryResult}->{TotalResultCount};

    if ($count == $pagesize ) {
        die "We must get more stories but I haven't implemented this!!!";
    }

    my @user_stories=();

    print STDERR "Found $count results\n" if $DEBUG;
    if ($count > 0 ) {
        foreach my $story_result (@{$result->{QueryResult}->{Results}}) {
            my $story = WebService::RallyDev::UserStory->new($story_result) || die "Cannot Instantiate User Story";
            my $tasks=$self->get_tasks_for_story({
                                                    story_url => $story->url,
                                                });
            print STDERR $story->id." - ".$story->Name." (".$story->Owner.")\n" if $DEBUG;
            #foreach my $story_task (@{$story->Tasks}) {
                #my $url = $story_task->{_ref};
                #my $task = $self->get_task($url);
                #
                #push @tasks,$task if $task;
                #print STDERR "\t".$task->id." - ".$task->Name." (".$task->Owner.")\n";
            #}
            $story->child_tasks($tasks);
            push @user_stories, $story;
        }
    } else {

        warn "No User stories found for Iteration $iteration_url";
    }

    return \@user_stories;
}

sub get_task {
    my $self = shift;
    my $url = shift;

    my $result = $self->_GET_request($url);

    my $task = WebService::RallyDev::Task->new($result->{Task}) || die "Cannot instantiate Task";

    return $task;
}

sub get_tasks_for_story {
    my $self = shift;
    my $args = shift;

    my $story_url = $args->{story_url} || die "no story url\n";
    my $owner = $args->{owner};
    my $pagesize = $args->{pagesize} || 200;
    my $order = $args->{order} || 'Rank';

    my $query ='';
    if ($owner) {
        $query = "(( Owner = $owner ) and ( WorkProduct = $story_url ))";
    } else {
        $query = "( WorkProduct = $story_url )";
    }

    my %args = (
                    query=>$query,
                    pagesize=>$pagesize,
                    order=>$order,
                    fetch=>'true',
                );
    my $result = $self->_GET_request('task_url',\%args);
    #print STDERR Dumper($result);

    my $count = $result->{QueryResult}->{TotalResultCount};

    my @tasks=();

    print STDERR "Found $count results for user story\n" if $DEBUG;
    if ($count > 0 ) {
        foreach my $task_result (@{$result->{QueryResult}->{Results}}) {
            my $task = WebService::RallyDev::Task->new($task_result) || die "Cannot instantiate Task";
            push @tasks,$task;
        }
    }

    return \@tasks;
}

sub last_request_errors {
    my $self = shift;
    return $self->{_last_request_errors};
}

sub _GET_request {
    my $self = shift;
    my $url = shift;
    my $args = shift;

    my $get_url = $self->_build_url($url,$args);
    print STDERR "Requesting url $get_url" if $DEBUG > 1;

    my  $ua = LWP::UserAgent->new;
    my $req = GET $get_url;
    $req->authorization_basic($self->username,$self->password);

    my $res = $ua->request($req);
    if ($res->is_success) {
        my $content = $res->content;

        print STDERR "content : $content\n" if $DEBUG;

        my $result = from_json($content);
        print STDERR Dumper($result) if $DEBUG;
	return $result;
    } else {
        $self->{_last_request_errors}=['Request Failed'];
        return undef;
    }
}

sub _build_url {
    my $self = shift;
    my $url_type = shift;
    my $params = shift;

    my $url = $webservice_urls->{$self->subscription}{$url_type};

    assert($url,'No valid URL found');

    my $query_string;
    $query_string = $self->_build_query_string($params) if ($params);

    $url.='?'.$query_string if $query_string;

    return $url;
}

sub _build_query_string {
    my $self = shift;
    my $args = shift;

    my $query_string = URI::Query->new(%$args);

    return $query_string;
}


1;

